package com.hhkj.admin.bank.service.biz.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.collect.Lists;
import com.hhkj.admin.bank.common.component.creator.BankCreator;
import com.hhkj.admin.bank.common.component.handler.BankHandler;
import com.hhkj.admin.bank.common.cont.BankConst;
import com.hhkj.admin.bank.domain.Bank;
import com.hhkj.admin.bank.domain.BankRule;
import com.hhkj.admin.bank.model.dopo.BankDo;
import com.hhkj.admin.bank.model.dopo.BankPo;
import com.hhkj.admin.bank.model.extend.BankRuleErrorMatch;
import com.hhkj.admin.bank.model.req.BankReq;
import com.hhkj.admin.bank.model.req.BankRuleReq;
import com.hhkj.admin.bank.model.res.BankPublishRes;
import com.hhkj.admin.bank.model.res.BankRes;
import com.hhkj.admin.bank.service.base.BankRuleService;
import com.hhkj.admin.bank.service.base.BankService;
import com.hhkj.admin.bank.service.biz.BankBizService;
import com.hhkj.admin.bank.service.extend.BankExtendService;
import com.hhkj.admin.bank.service.extend.BankRuleExtendService;
import com.hhkj.admin.course.domain.Course;
import com.hhkj.admin.course.model.dopo.CoursePo;
import com.hhkj.admin.course.service.base.CourseService;
import com.hhkj.admin.course.service.extend.CourseBankTypeExtendService;
import com.hhkj.admin.course.service.extend.CourseExtendService;
import com.hhkj.admin.question.common.cont.QuestionImportConst;
import com.hhkj.admin.question.common.utils.QuestionImportTemplateUtils;
import com.hhkj.admin.question.common.utils.QuestionImportUtils;
import com.hhkj.admin.question.domain.Question;
import com.hhkj.admin.question.model.dopo.QuestionDo;
import com.hhkj.admin.question.model.dopo.QuestionPo;
import com.hhkj.admin.question.model.extend.QuestionExportModel;
import com.hhkj.admin.question.model.extend.QuestionImportModel;
import com.hhkj.admin.question.model.req.ExcelTypeCountReq;
import com.hhkj.admin.question.model.req.QuestionReq;
import com.hhkj.admin.question.service.base.QuestionService;
import com.hhkj.api.bank.service.biz.ApiBankBizService;
import com.ruoyi.common.base.biz.service.biz.BaseBizServiceImpl;
import com.ruoyi.common.cont.SysConst;
import com.ruoyi.common.exception.GlobalErrorListException;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.excl.model.ExcelCreateModel;
import com.ruoyi.common.utils.excl.utils.ExcelUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * BankBizServiceImpl
 *
 * @author HHKJ-SERVER-ZJ
 * @since 2023/6/8 11:32
 */
@Service
public class BankBizServiceImpl
        extends BaseBizServiceImpl<Bank, BankReq, BankRes, BankDo, BankPo, BankService, BankExtendService>
        implements BankBizService
{
    @Autowired
    private BankService service;
    @Autowired
    private BankExtendService extendService;
    @Autowired
    private CourseService courseService;
    @Autowired
    private CourseExtendService courseExtendService;
    @Autowired
    private CourseBankTypeExtendService courseBankTypeExtendService;
    @Autowired
    private ApiBankBizService apiBankBizService;
    @Autowired
    private BankRuleExtendService ruleExtendService;
    @Autowired
    private BankRuleService ruleService;

    @Autowired
    private QuestionService questionService;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BankPublishRes publish(BankReq req)
    {
        List<QuestionReq> questionGroupList
                = QuestionImportUtils.readExcelQuestionList(new ExcelTypeCountReq(req.getFile(), req.getFileUrl()));

        req.setCourseId(0L);
        req.setCourseParentId(0L);
        req.setCourseBankTypeId(0L);

        BankReq bankDo = BankCreator.createBank(
                questionGroupList,
                BankCreator.createBankName(req.getName()),
                req.getCourseId(),
                req.getCourseParentId(),
                req.getCourseBankTypeId(),
                req.getRemark()
        );
        bankDo.setPublishFlag(SysConst.Flag.IS.getFlag());
        // 保存题库信息
        extendService.save(bankDo);
        // 保存规则信息
        BankRes bankRes = BeanUtil.toBean(bankDo, BankRes.class);

        // 校验题目是否支持规则
        BankRuleReq ruleReq = req.getRuleReq();
        ruleReq.setCourseId(req.getCourseId());
        ruleReq.setCourseParentId(req.getCourseParentId());
        ruleReq.setCourseBankTypeId(req.getCourseBankTypeId());
        ruleService.save(ruleReq);
        List<String> errorList = new ArrayList<>();
        List<BankReq> bankReqList = new ArrayList<>();
        BankHandler.handlerBankByRule(bankRes, ruleReq, questionGroupList, errorList, bankReqList);
        for (BankReq bankReq : bankReqList) {
            bankReq.setPublishFlag(SysConst.Flag.IS.getFlag());
            bankReq.setRuleId(ruleReq.getId());
        }
        extendService.saveBatch(bankReqList);
        BankPublishRes result = new BankPublishRes();
        result.setTotalBankId(bankRes.getId());
        for (BankReq bankReq : bankReqList) {
            if (BankConst.Type.PRACTISE.getCode().equals(bankReq.getTypeCode())) {
                result.setPractiseBankId(bankReq.getId());
            } else {
                result.setTestBankId(bankReq.getId());
            }
        }
        return result;
    }

    @Override
    public List<BankRes> listAllCoursePublishOfRule(BankReq req)
    {
        // 根据抽屉规则获取所需信息，对应得子规则id列表 / 对应科目列表
        BankRule rulePo = ruleExtendService.getPoByIdNotNull(req.getRuleId());
        List<Long> ruleIdList = new ArrayList<>();
        ruleIdList.add(rulePo.getId());
        if (rulePo.hasCourseChildrenRule()) {
            List<Long> childrenIdList
                    = ruleExtendService.listPoByParentId(req.getRuleId())
                    .stream().map(BankRule::getId)
                    .collect(Collectors.toList());
            ruleIdList.addAll(childrenIdList);
        }
        // 获取对应科目列表
        List<Course> coursePoList
                = courseExtendService.listByParentId(rulePo.getCourseParentId());

        // 获取题库信息列表
        Map<Long, BankRes> bankPoMapByCourseId = extendService.listPublishPoByRuleIdList(ruleIdList, req.getTypeCode())
                .stream()
                .map(po -> BeanUtil.toBean(po, BankRes.class))
                .collect(Collectors.groupingBy(BankRes::getCourseId,
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                CollectionUtil::getFirst
                        )));
        // 列表信息
        List<BankRes> resultList = new ArrayList<>();
        BankRes unPublishBank;
        for (Course coursePo : coursePoList) {
            if (bankPoMapByCourseId.containsKey(coursePo.getId())) {
                resultList.add(bankPoMapByCourseId.get(coursePo.getId()));
            } else {
                unPublishBank = new BankRes();
                unPublishBank.setCourseName(coursePo.getName());
                unPublishBank.setPublishFlag(SysConst.Flag.NO.getFlag());
                resultList.add(unPublishBank);
            }
        }
        // 排序
        resultList.sort((o1, o2) -> o2.getPublishFlag().compareTo(o1.getPublishFlag()));
        return resultList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBankByRule(BankRuleReq bankRuleReq)
    {
        // 获取上线的对应类型题目信息\获取各个题目的题库信息
        List<BankRes> totalBankList = extendService.listResIncludeQuestionListByRule(bankRuleReq);
        if (CollectionUtil.isEmpty(totalBankList)) {
            return;
        }
        // 题目规则信息分组获取
        boolean isGroupRule = !bankRuleReq.hasCourseChildrenRule();
        Map<Long, BankRule> ruleListGroupByCourseId = new HashMap<>();
        if (!isGroupRule) {
            ruleListGroupByCourseId = bankRuleReq.getChildren()
                    .stream()
                    .collect(Collectors.groupingBy(
                            BankRule::getCourseId,
                            Collectors.collectingAndThen(Collectors.toList(), CollectionUtil::getFirst)));
        }

        // 对上架的题库进行重新获取对应的题目处理
        BankRule courseBankRule;
        List<BankRuleErrorMatch> errorList = new ArrayList<>();
        List<BankReq> bankReqList = new ArrayList<>();
        List<Long> bankIdList = new ArrayList<>();
        List<QuestionReq> totalQuestionGroupList;
        List<String> courseErrorList;
        for (BankRes bankRes : totalBankList) {
            courseErrorList = new ArrayList<>();
            totalQuestionGroupList = BeanUtil.copyToList(bankRes.getQuestionGroupList(), QuestionReq.class);
            courseBankRule
                    = isGroupRule ? bankRuleReq : ruleListGroupByCourseId.getOrDefault(bankRes.getCourseId(), bankRuleReq);

            // 题库信息创建
            BankHandler.handlerBankByRule(bankRes, courseBankRule, totalQuestionGroupList, courseErrorList, bankReqList);

            if (CollectionUtil.isNotEmpty(courseErrorList)) {
                errorList.add(new BankRuleErrorMatch(bankRes, courseErrorList));
            }

            bankIdList.add(bankRes.getId());
        }

        if (CollectionUtil.isNotEmpty(errorList)) {
            throw new GlobalErrorListException(errorList);
        }
        extendService.removeByParentIdList(bankIdList);
        extendService.saveBatch(bankReqList);
        // 清空题库缓存信息
        apiBankBizService.clearBankCache(bankRuleReq.getCourseParentId(), bankRuleReq.getCourseBankTypeId());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updatePublishFlag(Long id, Integer publishFlag)
    {
        Bank bankPo = extendService.getPoByIdNotNull(id);
        if (publishFlag.equals(bankPo.getPublishFlag())) {
            if (SysConst.Flag.IS.getFlag().equals(publishFlag)) {
                throw new GlobalException("题库已上架");
            } else {
                throw new GlobalException("题库已下架");
            }
        }
        // 上架检查是否有规则
        BankRule rulePo
                = ruleExtendService.getPoByCourseIdAndCourseBankTypeIdNotNull(bankPo.getCourseId(), bankPo.getCourseBankTypeId());
        // 如果是上架，需要下架掉其他的
        if (SysConst.Flag.IS.getFlag().equals(publishFlag)) {
            extendService.updateBatchPublishFlagByCourseIdAndCourseBankType(
                    bankPo.getCourseId(),
                    bankPo.getCourseBankTypeId(),
                    SysConst.Flag.NO.getFlag());
            BankRes bankRes = BeanUtil.toBean(bankPo, BankRes.class);
            extendService.prepareBeforePublish(bankRes, rulePo);
        }

        // 上架总题库信息
        LambdaUpdateWrapper<Bank> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Bank::getId, id)
                .set(Bank::getPublishFlag, publishFlag);
        boolean result = service.update(updateWrapper);
        if (result) {
            updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(Bank::getParentId, id)
                    .eq(SysConst.Flag.IS.getFlag().equals(publishFlag), Bank::getRuleId, rulePo.getId())
                    .set(Bank::getPublishFlag, publishFlag);
            service.update(updateWrapper);
            apiBankBizService.clearBankCache(bankPo.getCourseParentId(), bankPo.getCourseId(), rulePo.getCourseBankTypeId());
        }
        return result;
    }

    @Override
    public boolean updateName(Long id, String name)
    {
        extendService.getByIdNotNull(id);
        // 验证名称是否重复
        LambdaQueryWrapper<Bank> bankQuery = new LambdaQueryWrapper<>();
        bankQuery.ne(Bank::getId, id)
                .eq(Bank::getName, name);
        long repeatNameAmount = service.count(bankQuery);
        if (repeatNameAmount > 0L) {
            throw new GlobalException("名称重复");
        }
        // 修改更新
        LambdaUpdateWrapper<Bank> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Bank::getId, id)
                .set(Bank::getName, name);
        return service.update(updateWrapper);
    }

    @Override
    public Workbook createBankQuestionImportTemplate(BankReq req)
    {
        Course courseDo
                = new Course(req.getCourseId(), req.getCourseParentId(), SysConst.Flag.NO.getFlag());

        List<CoursePo> coursePoList
                = courseService.listLevelNumThirdPoByDo(courseDo);
        // 创建模板信息
        if (ObjectUtil.isNull(coursePoList)) {
            throw new GlobalException("无可用科目信息");
        }
        List<ExcelCreateModel> modelList = new ArrayList<>();
        for (Course course : coursePoList) {
            modelList.add(new ExcelCreateModel(course.getName(),
                    QuestionImportModel.TITLE_NAME,
                    new ArrayList<>(QuestionImportConst.QUESTION_DEMO_LIST),
                    QuestionImportModel.class));
        }
        return getWorkbookConfigTemplate(modelList);
    }

    @Override
    public Workbook createExportQuestion(BankReq req) {
        if (ObjectUtil.isNull(req.getPublishFlag())) {
            // 默认导出上架的题库
            req.setPublishFlag(SysConst.Flag.IS.getFlag());
        }
        List<BankRes> bankResList = this.list(req);
        if (CollectionUtil.isEmpty(bankResList)) {
            throw new GlobalException("无导出数据");
        }
        // 所有的题库id
        List<Long> bankIdList = bankResList.stream().map(BankRes::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(bankIdList)) {
            throw new GlobalException("题库不存在");
        }
        QuestionDo questionDo = new QuestionDo();
        questionDo.setBankIdList(bankIdList);
        List<QuestionPo> questionList = questionService.listPoByDo(questionDo);
        if (CollectionUtil.isEmpty(questionList)) {
            throw new GlobalException("题目为空无导出数据");
        }
        Map<Long, List<Question>> bankQuestionListMap = questionList.stream().distinct().collect(Collectors.groupingBy(Question::getBankId));
        // 通过课程id分组 得到处理成课程id和题库id列表
        Map<Long, List<Long>> courseParentIdBankIdListMap = bankResList.stream().collect(Collectors.groupingBy(BankRes::getCourseId, Collectors.mapping(BankRes::getId, Collectors.toList())));
        // 所有课程名称
        Set<Long> courseParentIdSet = courseParentIdBankIdListMap.keySet();
        LambdaQueryWrapper<Course> courseQuery = new LambdaQueryWrapper<>();
        courseQuery.in(Course::getId, courseParentIdSet);
        courseQuery.select(Course::getId, Course::getName);
        List<Course> courseList = courseService.list(courseQuery);
        if (CollectionUtil.isEmpty(courseList)) {
            throw new GlobalException("课程类别列表不存在");
        }
        Map<Long, String> courseIdNameMap = courseList.stream().collect(Collectors.toMap(Course::getId, Course::getName));
        List<ExcelCreateModel> modelList = Lists.newArrayList();

        // 批量处理不同课程分不到不同sheet
        batchProcessBankIdListAssignSheet(modelList, courseIdNameMap, courseParentIdBankIdListMap, bankQuestionListMap);
        return getWorkbookConfigTemplate(modelList);
    }




    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(BankReq bank)
    {
        Course thirdCourse = null;
        // 校验科目id数据
        if (ObjectUtil.isNotNull(bank.getCourseId())) {
            thirdCourse = courseExtendService.getLevelNumThirdByIdNotDisable(bank.getCourseId());
        }
        // 校验类别id数据
        List<Course> thirdCourseList
                = courseExtendService.listLevelNumThirdByParentIdNotDisable(bank.getCourseParentId());
        courseBankTypeExtendService.getByCourseIdAndIdNotDisable(bank.getCourseParentId(), bank.getCourseBankTypeId());
        Map<String, List<QuestionImportModel>> questionModelListMapBySheetName
                = QuestionImportUtils.readMultipleModelListGroupByTypeCode(bank.getFile());

        if (CollectionUtil.isEmpty(questionModelListMapBySheetName)) {
            throw new GlobalException("文件读取失败，数据为空");
        }
        Map<String, List<QuestionReq>> questionListGroupBySheetName = QuestionImportUtils.convertGroupListImportMultipleGroup(questionModelListMapBySheetName);

        // 根据sheet名称获取对应科目id
        Set<String> importCourseNameSet = questionListGroupBySheetName.keySet();
        Set<String> thirdCourseNameSet
                = thirdCourseList.stream().map(Course::getName).collect(Collectors.toSet());

        if (thirdCourse == null) {
            if (!thirdCourseNameSet.containsAll(importCourseNameSet)) {
                throw new GlobalException("上传文件科目信息有误");
            }
        } else {
            Map<String, List<QuestionReq>> singleQuestionModelListMapBySheetName = new HashMap<>();
            for (Map.Entry<String, List<QuestionReq>> questionModelListMapBySheetNameEntry
                    : questionListGroupBySheetName.entrySet()) {
                if (CollectionUtil.isEmpty(singleQuestionModelListMapBySheetName)) {
                    singleQuestionModelListMapBySheetName.put(thirdCourse.getName(), questionModelListMapBySheetNameEntry.getValue());
                }
            }
            questionListGroupBySheetName = singleQuestionModelListMapBySheetName;
        }

        return this.save(thirdCourse, questionListGroupBySheetName, bank, thirdCourseList);
    }

    /**
     * 保存题库信息
     *
     * @param thirdCourse                  科目信息
     * @param questionListGroupBySheetName 题目列表分组
     * @param bank                         题库信息请求参数
     * @param thirdCourseList              科目列表
     * @return true/false
     */
    private boolean save(Course thirdCourse, Map<String, List<QuestionReq>> questionListGroupBySheetName, Bank bank, List<Course> thirdCourseList)
    {
        // 指定对应科目信息
        if (thirdCourse != null) {
            // 获取今天新增了几次对应科目的题库
            Course todayBankAmount = courseService.getTodayBankAmountById(thirdCourse.getId());
            BankReq bankReq = BankCreator.createBank(
                    questionListGroupBySheetName.get(thirdCourse.getName()),
                    BankCreator.createBankName(thirdCourse.getName(), (todayBankAmount == null ? 0 : todayBankAmount.getTodayBankAmount())),
                    thirdCourse.getId(), thirdCourse.getParentId(), bank.getCourseBankTypeId(), bank.getRemark());
            // 保存题库信息
            return extendService.save(bankReq);
        } else {
            // 科目id 根据名称
            Map<String, Long> thirdCourseIdMapByName = thirdCourseList.stream()
                    .collect(Collectors.groupingBy(Course::getName,
                            Collectors.collectingAndThen(Collectors.toList(), sameNameList -> {
                                Course course = CollectionUtil.getFirst(sameNameList);
                                return course.getId();
                            })));
            String sheetName;
            List<BankReq> bankDoList = new ArrayList<>();
            // 题库批次
            Map<Long, Integer> todayBankAmountMapById = courseService.listTodayBankAmountByIdList(new ArrayList<>(thirdCourseIdMapByName.values()))
                    .stream()
                    .collect(Collectors.toMap(Course::getId, Course::getTodayBankAmount));

            Long courseId;
            for (Map.Entry<String, List<QuestionReq>> questionListMapBySheetNameEntry
                    : questionListGroupBySheetName.entrySet()) {
                sheetName = questionListMapBySheetNameEntry.getKey();
                if (!thirdCourseIdMapByName.containsKey(sheetName)) {
                    continue;
                }
                courseId = thirdCourseIdMapByName.get(sheetName);
                BankReq bankReq = BankCreator.createBank(questionListMapBySheetNameEntry.getValue(),
                        BankCreator.createBankName(sheetName, (todayBankAmountMapById.getOrDefault(courseId, 0))),
                        courseId, bank.getCourseParentId(), bank.getCourseBankTypeId(), bank.getRemark());
                bankDoList.add(bankReq);
            }
            return extendService.saveBatch(bankDoList);
        }
    }

    @Override
    protected void beforeRemove(List<Long> idList)
    {
        // 检查题库是否已上架
        LambdaQueryWrapper<Bank> bankQuery = new LambdaQueryWrapper<>();
        bankQuery.in(Bank::getId, idList)
                .eq(Bank::getPublishFlag, SysConst.Flag.IS.getFlag());
        long publishBankAmount = service.count(bankQuery);
        if (publishBankAmount > 0L) {
            throw new GlobalException("题库已上架，无法删除");
        }
        long count = service.count(new LambdaQueryWrapper<Bank>()
                .in(Bank::getId, idList));
        if ((int) count != idList.size()) {
            throw new GlobalException("数据错误，刷新页面后重试");
        }
    }

    /**
     * 删除后置方法
     *
     * @param idList id列表
     */
    @Override
    protected void afterRemove(List<Long> idList)
    {
        extendService.removeByParentIdList(idList);
    }


    /**
     * 填充sheet内容并且调整整体风格
     * @param modelList
     * @return
     */
    private static Workbook getWorkbookConfigTemplate(List<ExcelCreateModel> modelList) {
        Workbook template = ExcelUtils.createExcelWorkbookMultipleSheet(modelList);
        QuestionImportTemplateUtils.supplySelectList(template);
        //设置表格格式
        QuestionImportTemplateUtils.formatHeader(template);
        return template;
    }

    /**
     * 批量处理不同课程分不到不同sheet
     * @param modelList 需要导出的excel数据
     * @param courseIdNameMap 课程名称map
     * @param courseParentIdBankIdListMap 课程名与具体题库map
     * @param bankQuestionMap 题库id和题库列表
     */
    private void batchProcessBankIdListAssignSheet(List<ExcelCreateModel> modelList, Map<Long, String> courseIdNameMap, Map<Long, List<Long>> courseParentIdBankIdListMap, Map<Long, List<Question>> bankQuestionMap) {
        courseParentIdBankIdListMap.forEach((courseParentId, bankIdList) -> {
            List<Question> questionList = getKeysByMap(bankQuestionMap, bankIdList);
            String courseName = courseIdNameMap.get(courseParentId);
            List<QuestionExportModel> questionExportModelList = QuestionImportUtils.transformEntityToModel(questionList);
            modelList.add(new ExcelCreateModel(courseName, QuestionImportModel.TITLE_NAME, questionExportModelList, QuestionExportModel.class));
        });
    }

    /**
     * 通过题库id列表获取题库列表
     * @param bankQuestionMap
     * @param bankIdList
     * @return
     */
    private List<Question> getKeysByMap(Map<Long, List<Question>> bankQuestionMap, List<Long> bankIdList){
        List<Question> questionList = Lists.newArrayList();
        bankIdList.forEach(bankId -> {
            List<Question> questionPoList = bankQuestionMap.get(bankId);
            if (CollectionUtil.isNotEmpty(questionPoList)) {
                questionList.addAll(questionPoList);
            }
        });
        return questionList;
    }
}
